/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package models;

import java.util.*;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import play.data.validation.Required;
import play.db.jpa.Model;

/**
 * 
 * @author Ousama
 */
@Entity
public class Idea extends Model {

    /****************************** RELATIONS! ********************************************/
    @ManyToOne
    public IdeaDeveloper creator;
    @ManyToMany
    @JoinTable(name = "Idea1")
    public List<IdeaDeveloper> spam = new ArrayList<IdeaDeveloper>();
    // @ManyToOne error ?
    // public IdeaDeveloper promoter;
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Idea2")
    public List<IdeaDeveloper> vote = new ArrayList<IdeaDeveloper>();
    @ManyToOne
    public Organization ideaOrg = null;
    @ManyToOne
    public Entities ideaEnt = null;
    @ManyToOne
    public Topic ideaTopic = null;
    @ManyToOne
    public ActionPlan ideaPlan = null;
    @OneToMany(mappedBy = "idea", cascade = CascadeType.ALL)
    public List<Comment> comments = new ArrayList<Comment>();
    @ManyToMany
    public List<Tag> tags = new ArrayList<Tag>();
    @ManyToMany
    public List<ActionItem> actionItemIdea = new ArrayList<ActionItem>();
    @OneToMany(mappedBy = "idea", cascade = CascadeType.ALL)
    public List<Rate> rates = new ArrayList<Rate>();
    @ManyToMany
    @JoinTable(name = "Duplicates1")
    public List<Idea> duplicates = new ArrayList<Idea>();
    @ManyToMany
    @JoinTable(name = "Duplicates2")
    public List<Idea> duplicatesOf = new ArrayList<Idea>();
    /****************************** ATTRIBUTES! ********************************************/
    public String title;
    public String description;
    public boolean published;
    public int numberOfViews;
    public Date dateOfDraftingPosting;
    public boolean promotedForExecution;
    public String priority = "Medium";
    public int noOfVotes;

    /**
     * creates a new idea in organization
     *
     * @author Amira
     * @param dev
     *            the idea developer creating the idea
     * @param org
     *            the organization where the idea lies
     * @return the idea created
     */
    public static Idea create(IdeaDeveloper dev, Organization org) {
        Idea i = new Idea();
        i.creator = dev;
        i.ideaOrg = org;
        i.save();
        dev.createdIdeas.add(i);
        dev.save();
        org.ideas.add(i);
        org.save();

        Log log = new Log("^ created ^ in ^.");
        log.ids = dev.id + "," + i.id + "," + org.id;
        log.types = "developer,idea,organization";
        log.save();

        return i;
    }

    /**
     * Constructor for Idea
     *
     * @author
     * @param title
     *            the title for the idea
     * @param description
     *            the description explaining the idea
     * @param ideaDeveloperId
     *            the idea developer creating the idea
     */
    public Idea(String title, String description, long ideaDeveloperId) {
        IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper.find("id=?", ideaDeveloperId).fetch().get(0);
        this.description = description;
        this.title = title;
        this.creator = ideaDeveloper;
        this.published = false;
        this.promotedForExecution = false;
        this.dateOfDraftingPosting = new Date();
        this.numberOfViews = 0;
        this.noOfVotes = 0;
        this.priority = "Medium";
//        ideaDeveloper.createdIdeas.add(this);
//        ideaDeveloper.save();

        Log log = new Log("^ created ^.");
        log.ids = ideaDeveloperId + "," + this.id;
        log.types = "developer,idea";
        log.save();

    }

    /**
     * Empty Constructor for Idea
     *
     * @author
     *
     */
    public Idea() {
    }

    /**
     * creates a new idea in an entity without a topic
     *
     * @Author Amira
     *
     * @param title
     *            the title of the idea
     *
     * @param description
     *            the description of the idea
     *
     * @param ideaDeveloperId
     *            the id of the idea developer who created their idea
     *
     * @param entityId
     *            the id of entity where the idea will be returned
     * @return the created idea
     */
    public static Idea createIdeaEntity(String title, String description,
            long ideaDeveloperId, long entityId) {
        Idea idea = new Idea(title, description, ideaDeveloperId);
        idea.save();
        Entities entity = (Entities) Entities.find("id=?", entityId).fetch().get(0);
        IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper.find("id=?", ideaDeveloperId).fetch().get(0);
        idea.ideaEnt = entity;
        idea.save();
        ideaDeveloper.createdIdeas.add(idea);
        ideaDeveloper.communityCounter = ideaDeveloper.communityCounter + 5;
        ideaDeveloper.save();
        entity.ideas.add(idea);
        entity.save();

        Log log = new Log("^ created ^ in ^.");
        log.ids = ideaDeveloperId + "," + idea.id + "," + entityId;
        log.types = "developer,idea,entity";
        log.save();

        return idea;
    }

    /**
     * creates a new idea in topic
     *
     * @Author Amira
     *
     * @param title
     *            the title of the idea
     *
     * @param description
     *            the description of the idea
     *
     * @param ideaDeveloperId
     *            the id of the idea developer who created this idea
     *
     * @param topicId
     *            the id of topic where the idea will be returned
     * @return the idea created
     */
    public static Idea createIdeaTopic(String title, String description,
            long ideaDeveloperId, long topicId) {
        Idea idea = new Idea(title, description, ideaDeveloperId);
        idea.save();
        Topic topic = (Topic) Topic.find("id=?", topicId).fetch().get(0);
        IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper.find("id=?", ideaDeveloperId).fetch().get(0);
        idea.ideaTopic = topic;
        idea.save();
        ideaDeveloper.createdIdeas.add(idea);
        ideaDeveloper.communityCounter = ideaDeveloper.communityCounter + 5;
        ideaDeveloper.save();
        topic.ideas.add(idea);
        topic.save();

        Log log = new Log("^ created ^ in ^.");
        log.ids = ideaDeveloperId + "," + idea.id + "," + topicId;
        log.types = "developer,idea,topic";
        log.save();

        return idea;
    }

    /**
     * creates a new idea in an organization without a topic
     *
     * @Author Amira
     *
     * @param title
     *            the title of the idea
     *
     * @param description
     *            the description of the idea
     *
     * @param ideaDeveloperId
     *            the id of the idea developer who created this idea
     *
     * @param orgId
     *            the id of organization where the idea will be returned
     * @return the created Idea
     */
    public static Idea createIdeaOrg(String title, String description,
            long ideaDeveloperId, long orgId) {
        Idea idea = new Idea(title, description, ideaDeveloperId);
        idea.save();
        Organization org = (Organization) Organization.find("id=?", orgId).fetch().get(0);
        IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper.find("id=?", ideaDeveloperId).fetch().get(0);
        idea.ideaOrg = org;
        idea.save();
        ideaDeveloper.createdIdeas.add(idea);
        ideaDeveloper.communityCounter = ideaDeveloper.communityCounter + 5;
        ideaDeveloper.save();
        org.ideas.add(idea);
        org.save();

        Log log = new Log("^ created ^ in ^.");
        log.ids = ideaDeveloperId + "," + idea.id + "," + orgId;
        log.types = "developer,idea,organization";
        log.save();

        return idea;
    }

    /**
	 * creates idea in the Action plan
	 * 
	 * @author Amira
	 * @param dev
	 *            the idea developer creating the idea
	 * @param plan
	 *            the action plan where the idea is being created
	 * @return the created idea
	 */
    public static Idea create(IdeaDeveloper dev, ActionPlan plan) {
        Idea i = new Idea();
        i.creator = dev;
        i.ideaPlan = plan;
        i.save();
        dev.createdIdeas.add(i);
        dev.save();
        plan.ideas.add(i);
        plan.save();

        Log log = new Log("^ created ^ in ^.");
        log.ids = dev.id + "," + i.id + "," + plan;
        log.types = "developer,idea,actionplan";
        log.save();

        return i;
    }

    /**
     * helper delete that deletes the idea and all its components
     *
     * @author amira
     */
    public void delete2() {
       if (this.ideaTopic != null) {
           for (int i = 0; i < this.ideaTopic.followed.size(); i++) {
               if (!this.ideaTopic.followed.get(i).equals(this.creator)) {
                   Notification.notify(" has deleted idea " + this.title,
                           this.ideaTopic.followed.get(i), this.creator,
                           new Date());
               }

           }

       }
       if (this.ideaEnt != null) {
           for (int i = 0; i < this.ideaEnt.followed.size(); i++) {
               if (!this.ideaEnt.followed.get(i).equals(this.creator)) {
                   Notification.notify(" has deleted idea " + this.title,
                           this.ideaEnt.followed.get(i), this.creator,
                           new Date());
               }

           }

       }
       if (this.ideaOrg != null) {
           for (int i = 0; i < this.ideaOrg.followed.size(); i++) {
               if (!this.ideaOrg.followed.get(i).equals(this.creator)) {
                   Notification.notify(" has deleted idea " + this.title,
                           this.ideaOrg.followed.get(i), this.creator,
                           new Date());
               }

           }

       }

       // deletes it from the actionItems
       for (int i = 0; i < actionItemIdea.size(); i++) {
           actionItemIdea.get(i).ideas.remove(this);
           actionItemIdea.get(i).save();
       }
       // deletes the tags relation
       for (int i = 0; i < tags.size(); i++) {
           this.removeTag(tags.get(i).name);
           // tags.get(i).ideas.remove(this);
           // tags.get(i).save();
       }

       // deletes the comments
       while(!comments.isEmpty()){
           comments.get(0).delete2();
       }
       // delete from the creator created ideas
       creator.createdIdeas.remove(this);
       creator.save();
       // delete the spam vots
       for (int i = 0; i < spam.size(); i++) {
           spam.get(i).spamIdea.remove(this);
           spam.get(i).save();
       }
       // delete the votes votes
       for (int i = 0; i < vote.size(); i++) {
           vote.get(i).voteIdea.remove(this);
           vote.get(i).save();
       }
       while(!rates.isEmpty()){
           rates.get(0).delete2();
       }
       // deletes it from its place :)
       if (ideaOrg != null) {
           ideaOrg.ideas.remove(this);
           ideaOrg.save();
       } else if (ideaEnt != null) {
           ideaEnt.ideas.remove(this);
           ideaEnt.save();
       } else if (ideaTopic != null) {
           ideaTopic.ideas.remove(this);
           ideaTopic.save();
       } else if (ideaPlan != null) {
           ideaPlan.ideas.remove(this);
           ideaPlan.save();
       }
       for (int i = 0; i < duplicates.size(); i++) {
           this.duplicates.get(i).duplicatesOf.remove(this);
           this.duplicates.get(i).save();
       }
       for (int i = 0; i < duplicatesOf.size(); i++) {
           this.duplicatesOf.get(i).duplicates.remove(this);
           this.duplicatesOf.get(i).save();
       }



       Log log = new Log("^ was deleted.");
       log.ids = this.id + "";
       log.types = "idea";
       log.save();
this.delete();
   }
    
    /**
     * post an idea whether it was drafted first or not
     *
     * @author :Amira
     */
    public void postAnIdea() {
        this.published = true;
        this.dateOfDraftingPosting = new Date();
        this.save();
        if (this.ideaTopic != null) {
            for (int i = 0; i < this.ideaTopic.followed.size(); i++) {
                if (!this.ideaTopic.followed.get(i).equals(this.creator)) {
                    Notification.notify(" has posted idea " + this.title,
                            this.ideaTopic.followed.get(i), this.creator,
                            new Date());
                }

            }

        }
        if (this.ideaEnt != null) {
            for (int i = 0; i < this.ideaEnt.followed.size(); i++) {
                if (!this.ideaEnt.followed.get(i).equals(this.creator)) {
                    Notification.notify(" has posted idea " + this.title,
                            this.ideaEnt.followed.get(i), this.creator,
                            new Date());
                }

            }

        }
        if (this.ideaOrg != null) {
            for (int i = 0; i < this.ideaOrg.followed.size(); i++) {
                if (!this.ideaOrg.followed.get(i).equals(this.creator)) {
                    Notification.notify(" has posted idea " + this.title,
                            this.ideaOrg.followed.get(i), this.creator,
                            new Date());
                }

            }

        }
        creator.numberOfIdeasPosted ++;
        creator.save();
    }
    /**
     * creates an idea in an entity
     * 
     * @author Amira
     * @param dev
     * 			the idea developer that create the idea
     * @param org
     * 			the entity where the idea will be created there
     * @return the created idea
     */

    public static Idea create(IdeaDeveloper dev, Entities org) {
        Idea i = new Idea();
        i.creator = dev;
        i.ideaEnt = org;
        i.save();
        dev.createdIdeas.add(i);
        dev.save();
        org.ideas.add(i);
        org.save();

        Log log = new Log("^ created ^ in ^.");
        log.ids = dev.id + "," + i.id + "," + org.id;
        log.types = "developer,idea,entity";
        log.save();

        return i;
    }
    /**
     * creates idea in a topic
     * @author Amira
     * @param dev
     * 			idea developer that creates the idea
     * @param org
     * 			the topic where idea will be created
     * @return the idea created
     */

    public static Idea create(IdeaDeveloper dev, Topic org) {
        Idea i = new Idea();
        i.creator = dev;
        i.ideaTopic = org;
        i.save();
        dev.createdIdeas.add(i);
        dev.save();
        org.ideas.add(i);
        org.save();

        Log log = new Log("^ created ^ in ^.");
        log.ids = dev.id + "," + i.id + "," + org.id;
        log.types = "developer,idea,topic";
        log.save();

        return i;
    }

    /**
     * report an idea as spam
     *
     * @author :Amira
     *
     * @param ideaDeveloperId
     *            the id of the ideadeveloper who reported this idea
     */
    public void reportAsSpam(long ideaDeveloperId) {
        IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper.find("id=?", ideaDeveloperId).fetch().get(0);
        this.spam.add(ideaDeveloper);
        this.save();
        ideaDeveloper.spamIdea.add(this);
        ideaDeveloper.save();

        Log log = new Log("^ reported ^ as spam.");
        log.ids = ideaDeveloperId + "," + this.id;
        log.types = "developer,idea";
        log.save();
        if (this.ideaTopic != null) {
            for (int i = 0; i < this.ideaTopic.managers.size(); i++) {
                Notification.notify(" has reported idea " + this.title + " as spam.",
                        this.ideaTopic.managers.get(i).ideaDeveloper, ideaDeveloper,
                        new Date());
            }

        }
        if (this.ideaEnt != null) {
            for (int i = 0; i < this.ideaEnt.managers.size(); i++) {
                Notification.notify(" has reported idea " + this.title + " as spam.",
                        this.ideaEnt.managers.get(i).ideaDeveloper, ideaDeveloper,
                        new Date());
            }



        }
        if (this.ideaOrg != null) {
            for (int i = 0; i < this.ideaOrg.managers.size(); i++) {
                Notification.notify(" has reported idea " + this.title + " as spam.",
                        this.ideaOrg.followed.get(i), ideaDeveloper,
                        new Date());
            }



        }

    }

    /**
     * non-static instance method to vote for a specific idea
     *
     * @author: Dina
     *
     * @param ideaDeveloperId
     *             it takes the IdeaDeveloper id that is voting
     */
    public void vote(long ideaDeveloperId) {
        IdeaDeveloper iDev = IdeaDeveloper.findById(ideaDeveloperId);
        this.noOfVotes = this.noOfVotes + 1;
        // this.save();
        this.vote.add(iDev);
        this.save();
        iDev.voteIdea.add(this);
        // iDev.save();

        Notification.notify("has voted on" + this.title, this.creator, iDev, new Date());

        Log log = new Log("^ voted on ^.");
        log.ids = ideaDeveloperId + "," + this.id;
        log.types = "developer,idea";
        log.save();

    }

    /**
     * non-static instance method to unvote for a specific idea
     *
     * @author: Dina
     *
     * @param ideaDeveloperId
     *             it takes the IdeaDeveloper id that is voting
     */
    public void unvote(long ideaDeveloperId) {
        IdeaDeveloper iDev = IdeaDeveloper.findById(ideaDeveloperId);
        this.noOfVotes--;
        this.save();
        this.vote.remove(iDev);
        this.save();
        iDev.voteIdea.remove(this);

        iDev.save();
        Notification.notify("has removed his vote on" + this.title, this.creator, iDev, new Date());

        Log log = new Log("^ un-voted on ^.");
        log.ids = ideaDeveloperId + "," + this.id;
        log.types = "developer,idea";
        log.save();

    }

    /**
     * Checks if the given tag is already a tag of this idea and invokes the
     * method addTag() if it exits the while loop without returning true which
     * means the tag does not exist it is clear to go ahead and add the tag to
     * this idea
     *
     * @author Zein
     * @param String
     *            name of the tag to be added
     * @return Boolean, true if tag already exists, false otherwise
     */
    public boolean existingTag(String name) {
        int i = 0;
        while (i < this.tags.size()) {
            if (this.tags.get(i).name.equalsIgnoreCase(name)) {
                return true;
            }
            i++;
        }

        return false;
    }

    /**
     * Relates the given tag to this Idea and it is invoked from the
     * existingTag() method
     *
     * @author Zein
     * @param String
     *            name of the tag to be added
     */
    public void addTag(String name) {
        Tag thistag;
        List<Tag> tagIdea = Tag.find("name = ?", name).fetch();
        if (tagIdea.isEmpty()) {
            Tag newtag = new Tag(name);
            this.tags.add(newtag);
            newtag.addIdea(this);
            thistag = newtag;
        } else {
            this.tags.add(tagIdea.get(0));
            tagIdea.get(0).addIdea(this);
            thistag = tagIdea.get(0);
        }
        this.save();

        Log log = new Log("^ was added to ^.");
        log.ids = thistag.id + "," + this.id;
        log.types = "tag,idea";
        log.save();
    }

    /**
     * Removes the tag from the idea's tags list and removes the idea from the
     * tag's ideas list.
     *
     * @author Zein
     * @param String
     *            name of the tag to be removed
     */
    public void removeTag(String name) {
        Tag thistag = null;
        int i = 0;
        while (i < this.tags.size()) {
            if (this.tags.get(i).name.equalsIgnoreCase(name)) {
                thistag = this.tags.get(i);
                this.tags.get(i).removeIdea(this);
                this.tags.remove(this.tags.get(i));
            }
            i++;
        }
        this.save();

        Log log = new Log("^ was deleted from ^.");
        log.ids = thistag.id + "," + this.id;
        log.types = "tag,idea";
        log.save();

    }

    /**
     * promotes an idea by changing promotedForExecution = true
     *
     * @author Sameha
     */
    public void promoteIdea() {
        this.promotedForExecution = true;
        this.save();

        Log log = new Log("^ was promoted for execution.");
        log.ids = this.id + "";
        log.types = "idea";
        log.save();
    }

    /**
     * demotes the idea by making promotedForExecution = false
     *
     * @author Sameha
     */
    public void demoteIdea() {
        this.promotedForExecution = false;
        this.save();

        Log log = new Log("^ was demoted.");
        log.ids = this.id + "";
        log.types = "idea";
        log.save();
    }

    /**
     * finds the organization where the idea exists
     *
     * @author
     * @return the organization of the idea
     */
    public Organization orgWhereIdeaExist() {
        Organization org = new Organization();

        if (this.ideaOrg != null) {
            org = this.ideaOrg;
        }
        if (this.ideaEnt != null) {
            org = this.ideaEnt.liesIn;
        }
        if (this.ideaTopic != null) {
            org = this.ideaTopic.orgWhereTopicExist();
        }

        return org;
    }
    public int incrementNumberOfViews(){
        this.numberOfViews++;
        this.save();
        return this.numberOfViews;
    }
}
